package com.hontech.kaltaapp.store

import android.content.ContentValues
import android.database.sqlite.SQLiteDatabase
import com.hontech.kaltaapp.device.drawHeatCtrl
import com.hontech.kaltaapp.device.drawWater2
import com.hontech.kaltaapp.device.event.status
import com.hontech.kaltaapp.device.nozzleCtrl
import com.hontech.kaltaapp.device.partsCtrl
import com.hontech.kaltaapp.utils.runAsync
import kotlinx.coroutines.delay


const val COOKING_TYPE_WAIT = 0
const val COOKING_TYPE_WATER = 1
const val COOKING_TYPE_STEAM = 2
const val COOKING_TYPE_MOVE_STEAM = 3
const val COOKING_TYPE_ACTION = 4

abstract class CookingExecutable {

    abstract val type: Int
    abstract val name: String
    abstract suspend fun exec()
}

data class WaitInfo(
    override var name: String,
    var waitType: Int,
    var value: Int
) : CookingExecutable() {

    override val type = TYPE
    companion object {
        const val TABLE_NAME = "tb_wait_info"

        const val SQL_CREATE = "CREATE TABLE IF NOT EXISTS $TABLE_NAME(" +
                "id INTEGER PRIMARY KEY AUTOINCREMENT, " +
                "name TEXT NOT NULL, " +
                "wait_type INTEGER NOT NULL, " +
                "value INTEGER NOT NULL" +
                ")"

        const val TYPE = COOKING_TYPE_WAIT


        const val WAIT_WATER = 0
        const val WAIT_STEAM_TEMP = 1
        const val WAIT_STEAM_KPA = 2

        const val TIMEOUT = 30 * 1000

        suspend fun delete(id: Long) {
            val conn = Store.conn
            runAsync {
                conn.execSQL("DELETE FROM ${TABLE_NAME} WHERE id = ?", arrayOf(id))
            }
        }

        fun get(conn: SQLiteDatabase, id: Long): WaitInfo {
            val rows = conn.rawQuery("SELECT name, wait_type, value FROM $TABLE_NAME WHERE id = ? LIMIT 1", arrayOf(id.toString()))
            return rows.use {
                if (!it.moveToNext()) {
                    throw IllegalStateException("没有找到对于的wait_info")
                }
                val name = it.getString(0)
                val waitType = it.getInt(1)
                val value = it.getInt(2)
                WaitInfo(name, waitType, value)
            }
        }
    }

    private suspend fun waitWater() {
        drawHeatCtrl(3)
        partsCtrl(12, 0)

        try {
            var time_count = 0
            while (status().water.value < this.value) {
                delay(500)
                time_count += 500
                if (time_count > TIMEOUT) {
                    throw IllegalStateException("等待超时")
                }
            }
        } catch (e: Exception) {
            e.printStackTrace()
            throw e
        } finally {
            partsCtrl(12, 1)
            drawHeatCtrl(2)
        }
    }

    private suspend fun waitSteamTemp() {
        drawHeatCtrl(4)
        var time_count = 0
        while (status().steam.value < this.value) {
            delay(500)
            time_count += 500
            if (time_count > TIMEOUT) {
                throw IllegalStateException("等待超时")
            }
        }
    }

    private suspend fun waitSteamKpa() {
        drawHeatCtrl(4)
        var time_count = 0
        while (status().kpa < this.value) {
            delay(500)
            time_count += 500
            if (time_count > TIMEOUT) {
                throw IllegalStateException("等待超时")
            }
        }
    }

    override suspend fun exec() {
        when (type) {
            WAIT_WATER -> waitWater()
            WAIT_STEAM_TEMP -> waitSteamTemp()
            WAIT_STEAM_KPA -> waitSteamKpa()
            else -> throw IllegalStateException("未知等待类型")
        }
    }

    fun insert(conn: SQLiteDatabase): Long {
        val row = ContentValues()
        row.put("name", name)
        row.put("wait_type", waitType)
        row.put("value", value)
        return conn.insertOrThrow(TABLE_NAME, null, row)
    }
}


data class WaterInfo(
    override var name: String,
    var mm: Int, // 喷嘴位置
    var ml: Int, // 喷水体积
    var openHeat: Int // 0:关闭瞬热器, 1:打开瞬热器
) : CookingExecutable() {
    companion object {
        const val TABLE_NAME = "tb_water_info"
        const val SQL_CREATE = "CREATE TABLE IF NOT EXISTS $TABLE_NAME(" +
                "id INTEGER PRIMARY KEY AUTOINCREMENT, " +
                "name TEXT NOT NULL, " +
                "mm INTEGER NOT NULL, " +
                "ml INTEGER NOT NULL, " +
                "open_heat INTEGER NOT NULL" +
                ")"

        const val TYPE = COOKING_TYPE_WATER

        suspend fun delete(id: Long) {
            val conn = Store.conn
            runAsync {
                conn.execSQL("DELETE FROM ${TABLE_NAME} WHERE id = ?", arrayOf(id))
            }
        }

        fun get(conn: SQLiteDatabase, id: Long): WaterInfo {
            val rows = conn.rawQuery("SELECT name, mm, ml, open_heat FROM $TABLE_NAME WHERE id = ? LIMIT 1", arrayOf(id.toString()))
            return rows.use {
                if (!it.moveToNext()) {
                    throw IllegalStateException("没有找到water_info")
                }
                val name = it.getString(0)
                val mm = it.getInt(1)
                val ml = it.getInt(2)
                val openHeat = it.getInt(3)
                WaterInfo(name, mm, ml, openHeat)
            }
        }
    }

    override val type = TYPE

    override suspend fun exec() {
        if (openHeat == 1) {
            drawHeatCtrl(3)
            partsCtrl(12, 0)
        }

        nozzleCtrl(mm)
        drawWater2(ml)

        if (openHeat == 1) {
            partsCtrl(12, 1)
            drawHeatCtrl(2)
        }
    }

    fun insert(conn: SQLiteDatabase): Long {
        val row = ContentValues()
        row.put("name", name)
        row.put("mm", mm)
        row.put("ml", ml)
        row.put("open_heat", openHeat)
        return conn.insert(TABLE_NAME, null, row)
    }
}

data class SteamInfo(
    override var name: String,
    var mm: Int,
    var duration: Int
) : CookingExecutable() {

    companion object {
        const val TABLE_NAME = "tb_steam_info"
        const val SQL_CREATE = "CREATE TABLE IF NOT EXISTS $TABLE_NAME(" +
                "id INTEGER PRIMARY KEY AUTOINCREMENT, " +
                "name TEXT NOT NULL, " +
                "mm INTEGER NOT NULL, " +
                "duration INTEGER NOT NULL" +
                ")"

        const val TYPE = COOKING_TYPE_STEAM

        suspend fun delete(id: Long) {
            val conn = Store.conn
            runAsync {
                conn.execSQL("DELETE FROM ${TABLE_NAME} WHERE id = ?", arrayOf(id))
            }
        }

        fun get(conn: SQLiteDatabase, id: Long): SteamInfo {
            val rows = conn.rawQuery("SELECT name, mm, duration FROM $TABLE_NAME WHERE id = ? LIMIT 1", arrayOf(id.toString()))
            return rows.use {
                if (!it.moveToNext()) {
                    throw IllegalStateException("没有找到steam_info")
                }
                val name = it.getString(0)
                val mm = it.getInt(1)
                val duration = it.getInt(2)
                SteamInfo(name, mm, duration)
            }
        }
    }

    override val type = TYPE

    override suspend fun exec() {
        nozzleCtrl(mm)
        partsCtrl(7, 0)
        delay(duration * 1000L)
        partsCtrl(7, 1)
    }

    fun insert(conn: SQLiteDatabase): Long {
        val row = ContentValues()
        row.put("name", name)
        row.put("mm", mm)
        row.put("duration", duration)
        return conn.insertOrThrow(TABLE_NAME, null, row)
    }
}

data class MoveSteamInfo(
    override var name: String,
    var mm: Int,
    var mm2: Int,
    var duration: Int
) : CookingExecutable() {

    companion object {
        const val TABLE_NAME = "tb_move_steam_info"
        const val SQL_CREATE = "CREATE TABLE IF NOT EXISTS $TABLE_NAME(" +
                "id INTEGER PRIMARY KEY AUTOINCREMENT, " +
                "name TEXT NOT NULL, " +
                "mm INTEGER NOT NULL, " +
                "mm2 INTEGER NOT NULL, " +
                "duration INTEGER NOT NULL" +
                ")"

        const val TYPE = COOKING_TYPE_MOVE_STEAM

        suspend fun delete(id: Long) {
            val conn = Store.conn
            runAsync {
                conn.execSQL("DELETE FROM $TABLE_NAME WHERE id = ?", arrayOf(id))
            }
        }

        fun get(conn: SQLiteDatabase, id: Long): MoveSteamInfo {
            val rows = conn.rawQuery("SELECT name, mm, mm2, duration FROM $TABLE_NAME WHERE id = ? LIMIT 1", arrayOf(id.toString()))
            return rows.use {
                if (!it.moveToNext()) {
                    throw IllegalStateException("没有找到move_steam_info")
                }
                val name = it.getString(0)
                val mm = it.getInt(1)
                val mm2 = it.getInt(2)
                val duration = it.getInt(2)
                MoveSteamInfo(name, mm, mm2, duration)
            }
        }
    }

    override val type = TYPE

    override suspend fun exec() {
        var timecount = 0L
        val timeout = duration * 1000L
        nozzleCtrl(mm)
        partsCtrl(7, 0)
        while (timecount < timeout) {
            val start = System.currentTimeMillis()
            nozzleCtrl(mm2)
            nozzleCtrl(mm)
            timecount += System.currentTimeMillis() - start
        }
        partsCtrl(7, 1)
    }

    fun insert(conn: SQLiteDatabase): Long {
        val row = ContentValues()
        row.put("name", name)
        row.put("mm", mm)
        row.put("mm2", mm2)
        row.put("duration", duration)
        return conn.insertOrThrow(TABLE_NAME, null, row)
    }
}

data class Action(
    val id: Long,
    val name: String
)
data class CookingAction(
    override var name: String,
    val actions: ArrayList<CookingExecutable> = arrayListOf()
) : CookingExecutable() {

    companion object {
        const val TABLE_NAME = "tb_cooking_action"
        const val SQL_CREATE = "CREATE TABLE IF NOT EXISTS $TABLE_NAME(" +
                "id INTEGER PRIMARY KEY AUTOINCREMENT, " +
                "name TEXT NOT NULL UNIQUE" +
                ")"

        suspend fun get(): ArrayList<CookingAction> {

            val conn = Store.conn
            val actions = ArrayList<Action>()
            runAsync {

                val rows = conn.rawQuery("SELECT id, name FROM $TABLE_NAME", arrayOf())
                rows.use {
                    while (it.moveToNext()) {
                        val id = it.getLong(0)
                        val name = it.getString(1)
                        actions.add(Action(id, name))
                    }
                }
            }

            val list = ArrayList<CookingAction>()
            for (action in actions) {
                val cooking = CookingAction(action.name)
                val steps = CookingStep.getSteps(action.id)
                for (step in steps) {
                    val info = runAsync {
                        when (step.type) {
                            WaitInfo.TYPE -> WaitInfo.get(conn, step.infoId)
                            WaterInfo.TYPE -> WaterInfo.get(conn, step.infoId)
                            SteamInfo.TYPE -> SteamInfo.get(conn, step.infoId)
                            MoveSteamInfo.TYPE -> MoveSteamInfo.get(conn, step.infoId)
                            else -> throw IllegalStateException("invalid info type")
                        }
                    }
                    cooking.actions.add(info)
                }
                list.add(cooking)
            }

            return list
        }
    }

    override val type = COOKING_TYPE_ACTION

    override suspend fun exec() {
        for (action in actions) {
            action.exec()
        }
    }

    suspend fun save() {
        delete()
        val conn = Store.conn
        runAsync {
            conn.beginTransaction()
            try {
                val value = ContentValues()
                value.put("name", name)
                val actionId = conn.insertOrThrow(TABLE_NAME, null, value)

                for ((index, action) in actions.iterator().withIndex()) {
                    val infoId = when (action) {
                        is WaitInfo -> action.insert(conn)
                        is WaterInfo -> action.insert(conn)
                        is SteamInfo -> action.insert(conn)
                        is MoveSteamInfo -> action.insert(conn)
                        else -> throw IllegalStateException("未知info类型")
                    }
                    CookingStep.insert(conn, actionId, action.type, index, infoId)
                }
                conn.setTransactionSuccessful()
            } catch (e: Exception) {
                e.printStackTrace()
                throw e
            } finally {
                conn.endTransaction()
            }
        }
    }

    suspend fun delete() {
        val actionId = try {
            getId()
        } catch (e: Exception) {
            e.printStackTrace()
            return
        }
        val steps = CookingStep.getSteps(actionId)
        for (step in steps) {
            when (step.type) {
                WaitInfo.TYPE -> WaitInfo.delete(step.infoId)
                WaterInfo.TYPE -> WaterInfo.delete(step.infoId)
                SteamInfo.TYPE -> SteamInfo.delete(step.infoId)
                MoveSteamInfo.TYPE -> MoveSteamInfo.delete(step.infoId)
                else -> throw IllegalStateException("未知类型:${step.type}")
            }
        }
        CookingStep.delete(actionId)
        val conn = Store.conn
        runAsync {
            conn.execSQL("DELETE FROM $TABLE_NAME WHERE id = ?", arrayOf(actionId))
        }
    }

    suspend fun getId(): Long {
        val conn = Store.conn
        return runAsync {
            val rows = conn.rawQuery("SELECT id FROM $TABLE_NAME WHERE name = ? LIMIT 1", arrayOf(name))
            rows.use {
                if (!it.moveToNext()) {
                    throw IllegalStateException("not found action id")
                }
                it.getLong(0)
            }
        }
    }
}


data class CookingStep(
    val actionId: Long,
    val type: Int,
    val step: Int,
    val infoId: Long,
) {

    companion object {
        const val TABLE_NAME = "tb_cooking_step"
        const val SQL_STEP_CREATE = "CREATE TABLE IF NOT EXISTS $TABLE_NAME(" +
                "id INTEGER PRIMARY KEY AUTOINCREMENT, " +
                "action_id INTEGER NOT NULL, " +
                "type INTEGER NOT NULL, " +
                "step INTEGER NOT NULL, " +
                "info_id INTEGER NOT NULL" +
                ")"

        fun insert(conn: SQLiteDatabase, actionId: Long, type: Int, step: Int, infoId: Long) {
            val row = ContentValues()
            row.put("action_id", actionId)
            row.put("type", type)
            row.put("step", step)
            row.put("info_id", infoId)
            conn.insertOrThrow(TABLE_NAME, null, row)
        }

        suspend fun delete(actionId: Long) {
            val conn = Store.conn
            runAsync {
                conn.execSQL("DELETE FROM $TABLE_NAME WHERE action_id = ?", arrayOf(actionId))
            }
        }

        suspend fun getSteps(actionId: Long): ArrayList<CookingStep> {
            val conn = Store.conn
            return runAsync {
                val list = ArrayList<CookingStep>()

                val rows = conn.rawQuery("SELECT type, step, info_id FROM $TABLE_NAME WHERE action_id = ? ORDER BY step ASC",
                    arrayOf(actionId.toString()))
                rows.use {
                    while (it.moveToNext()) {
                        val type = it.getInt(0)
                        val step = it.getInt(1)
                        val infoId = it.getLong(2)
                        list.add( CookingStep(actionId, type, step, infoId) )
                    }
                }

                list
            }
        }
    }
}

